from base64 import encode

a = type(abs)
print(a)
# 输出 <class 'builtin_function_or_method'>
b = type(a)
print(b)
# 输出<class 'type'>

# 判断一个对象是否是函数怎么办,可以使用types
import types
def fn():
    pass
print(type(fn)==types.FunctionType)

# 对于class 的继承关系来说,使用type()就很不方便.我们要断定class的类型,可以使用isinstance()
# 继承关系 object -> Animal -> Dog -> Husky
# 先创建3种类型的对象
# a = Animal()
# d = Dog()
# h = Husky()
# isinstance(h)

# 使用dir
# 如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list，比如，获得一个str对象的所有属性和方法：
print(dir('ABC'))
print(len('ABC'))
print('ABC'.__len__())
# 返回小写的字符串
print('ABC'.lower())

# 仅仅把属性和方法列出来是不够的，配合getattr()、setattr()以及hasattr()，我们可以直接操作一个对象的状态：
class MyObject(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x
obj = MyObject()

print(hasattr(obj, 'x'))  # 有属性'x'吗?
print(obj.x)

print(setattr(obj, 'y', 19)) # 设置一个属性'y'

print(getattr(obj, 'y')) # 获取属性 'y'
print(obj.y)

# 给实例绑定一个方法
class Student(object):
    pass
s = Student()
s.name ="MICAEL"
print(s.name)
def set_age(self,age):
    self.age = age
from types import MethodType
s.set_age = MethodType(set_age,s)
s.set_age(25)
print(s.age)
def set_score(self,score):
     self.score = score
Student.set_score = set_score
s.set_age(100)
# print(s.score)

print(dir('ABC'))

# 使用__slots__
# 如果我们想要限制实例的属性怎么办？比如，只允许对Student实例添加name和age属性。
# 使用__slots__要注意，__slots__定义的属性仅对当前类实例起作用，对继承的子类是不起作用的：
class Students(object):
    __slots__ = ("name", 'age')
s = Students() # 创建新的实例
s.name = 'Michael'
s.age = 25
# s.score = 99   #这种方式是错误的

class GraduateStudent(Student):
    pass

g = GraduateStudent()
g.score = 9999
print(g.score)


# 多继承
# class Dog(Mammal, Runnable):
#     pass
# # Minxin 继承
# 我们把Runnable和Flyable改为RunnableMixIn和FlyableMixIn。
# class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
#     pass


# __str__ :直接显示变量调用
# __repr__():是调试服务的

# __str__ = __repr__
# 查看定制类


# 使用枚举类
# from enum import Enum
# Month = Enum('Month',('Jan','Feb','Mar'))
#
# for name, member in Month.__member__.items():
#     print(name,'=>',member,',', member.value)

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')


# StringIO
# 很多时候.数据写不一定是文件,也可能是在内存中读写
# stringio顾名思义就是在内存中读写str
# 要把str写入StringIO,我们需要先创建一个StringIo,然后,像文件一样写入即可:
from io import StringIO
f = StringIO()
print(f.write('hello'))
print(f.write(' '))
print(f.write('world!'))
print(f.write(' '))
print(f.write("ainimeishangliang"))
print(f.getvalue())
# getvalue()方法用于获得写入后的str。

# f = StringIO("Hello!\nHI!\nGoodbye!")
# while True:
#     s = f.readlines()
#     if s == '':
#         break
#     print(s.strip())

# stringio 操作的只能是str,如果要操作二进制数据,就需要使用BytesIO
# BytesIO 实现了在内存中读写bytes,我们创建一个BytesIO,然后写入一些bytes:
# from io import BytesIO
# f = BytesIO
# print(f.write('中文',encode('utf-8')))
# print(f.getvalue())


# 操作文件和目录
import os
print(os.name)
# 如果输出posix，说明系统是Linux,unix或Mac OS X，如果是nt,就是Windows系统
print('--------------------------------')
print(os.environ)
# 查看当前目录的绝对路径
print(os.path.abspath(','))
# 添加
print(os.path.join('/users/ma/ac/', 'testdir'))
# 然后创建一个目录
# print(os.mkdir('F:/ccc'))

# 删除一个目录
# os.rmdir('/sad/dasd/dasd')

#拆分
# os.path.split()

# 直接获得文件的扩展名
# os.path.splitext()

#对文件的重命名
# os.rename('1.txt', '2.txt')

#删除文件
# os.remove('2.txt')

# 例举当前目录下所有目录
print([x for x in os.listdir('.') if os.path.isdir(x)])

# 尝试把一个对象序列化并写入文件
import pickle
d = dict(name='Bob', age=20, score=88)
# pickle.dumps(d)
print(pickle.dumps(d))

# 常见的系统调用，其中就包括fork,可以在python程序中轻松创建子进程
# 调用getppid()就可以拿到父进程的ID
# import os
# print('process(%s) start...' % os.getpid())
# pid = os.fork()
# # windows上无os.fork() 可以使用multiproessing代替os.fork()
# if pid == 0:
#     print("I am chuld process (%s) and my parent is %s." % (os.getpid(), os.getppid()))
# else:
#     print('I (%s) just created a child process (%s).' % (os.getpid(), pid))

from multiprocessing import Process
import os


def run_proc(name):
    print("运行子进程%s(%s)......."%(name, os.getpid()))


if __name__ == '__main__':
    print('父进程%s' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('子进程将开始')
    p.start()
    p.join()
    print('子进程结束')

# os.getpid()获取当前进程id
# os.getppid()获取父进程id
# join()方法可以等待子进程结束后再继续往下运行，通常用于进程间的同步。

# 如果要启动大量的子进程，可以用进程池的方式批量创建子进程
from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')

# 子进程 subprocess可以非常方便的启动一个子进程，然后控制输入和输出
import subprocess

print('$ nslookup www.python.org')
r = subprocess.call(['nslookup','www.python.org'])
print('Exit code:', r)

# 进程之间的通信
# process之间肯定是需要通信的，操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制，提供了Queue,Pipes等多种方式来交换数据。
# 我们以Queue为例，在父进程中创建两个子进程，一个往Queue里写数据，一个从Queue里读数据：.

from multiprocessing import Process, Queue
import os, time, random

# 写数据进程执行的代码:
def write(q):
    print('Process to write: %s' % os.getpid())
    for value in ['A', 'B', 'C']:
        print('Put %s to queue...' % value)
        q.put(value)
        time.sleep(random.random())

# 读数据进程执行的代码:
def read(q):
    print('Process to read: %s' % os.getpid())
    while True:
        value = q.get(True)
        print('Get %s from queue.' % value)

if __name__=='__main__':
    # 父进程创建Queue，并传给各个子进程：
    q = Queue()
    pw = Process(target=write, args=(q,))
    pr = Process(target=read, args=(q,))
    # 启动子进程pw，写入:
    pw.start()
    # 启动子进程pr，读取:
    pr.start()
    # 等待pw结束:
    pw.join()
    # pr进程里是死循环，无法等待其结束，只能强行终止:
    pr.terminate()

# 多线程同时操作一个变量怎样把内容给改乱了：
import time, threading
# threading 多线程 ，其中threading中最核心的类为Thread
# 假定这是你的银行存款:
balance = 0
def change_it(n):
    # 先存后取，结果应该为0：
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(2000000):
        change_it(n)

# 第一个参数是线程函数变量，第二个参数args是一个数据变量参数，如果只传一个值,就只需要传递多个参数，那么还可以传递下去其他的参数，其他的逗号不能少，少了就不是数组了，就会出错。
t1 = threading.Thread(target=run_thread, args=(5, ))
t2 = threading.Thread(target=run_thread, args=(8, ))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

# 可以通过 Thread 的 is_alive() 方法查询线程是否还在运行。

# 两个线程顺序 价格会乱
# 初始值 balance = 0
# t1: x1 = balance + 5  # x1 = 0 + 5 = 5
# t2: x2 = balance + 8  # x2 = 0 + 8 = 8
# t2: balance = x2      # balance = 8
# t1: balance = x1      # balance = 5
# t1: x1 = balance - 5  # x1 = 5 - 5 = 0
# t1: balance = x1      # balance = 0
# t2: x2 = balance - 8  # x2 = 0 - 8 = -8
# t2: balance = x2      # balance = -8
# 结果 balance = -8



# 不让其冲突，需要创建一个锁就是通过threading.Lock()
balance = 0
lock = threading.Lock()
def change_it(n):
    # 先存后取，结果应该为0：
    global balance
    balance = balance + n
    balance = balance - n
def run_thread(n):
    for i in range(100000):
        # 先要获取锁
        lock.acquire()
        try:
            # 放心的改吧
            change_it(n)
        finally:
            lock.release()
t1 = threading.Thread(target=run_thread, args=(5, ))
t2 = threading.Thread(target=run_thread, args=(8, ))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
# 当多个线程同时执行lock.acquire()时，只有一个线程能成功地获取锁，然后继续执行代码，其他线程就继续等待直到获得锁为止。
# python的多线程是假的————因为有个 全局解释器锁（GIL）。

# 线程编程，模型复杂，容易发生冲突，必须用锁加以隔离，同时，又要小心死锁的发生。

# Python解释器由于设计时有GIL全局锁，导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。

# 一个ThreadLocal变量虽然是全局变量，但每个线程都只能读写自己线程的独立副本，互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。

# 分布式进程
# 在Thread和Process中，应当优选Process，因为Process更稳定，而且，Process可以分布到多台机器上，而Thread最多只能分布到同一台机器的多个CPU上。
import random, time, queue
from multiprocessing.managers import BaseManager
# 发送任务的队列
task_queue = queue.Queue()
# 接收结果的队列
result_queue = queue.Queue()

#从BaseManager继承的QueueManager:
class QueueManager(BaseManager):
    pass
#把两个Queue都注册到网络上，callable参数关联了Queue对象：
QueueManager.register('get_task_queue', callable=lambda: task_queue)
QueueManager.register('get_result_queue', callable=lambda: result_queue)
# 绑定端口5000， 设置验证码'abc'
manager = QueueManager(address=('', 5000), authkey=b'abc')
# 启动Queue
manager.start()
# 获得通过网络访问的Queue对象
task = manager.get_task_queue()
result = manager.get_result_queue()
# 放几个任务进去
for i in range(10):
    n = random.randint(0, 10000)
    print('Put task %d...' % n)
    task.put(n)
#从result队列中读取结果
print('Try get results...')
for i in range(10):
    r = result.get(timeout=10)
    print('Result: %s' %r)
# 关闭
manager.shutdown()
print('master exit')

